﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Notoric.Gui.Controls
{
    /// <summary>
    /// Editable Text Block.
    /// </summary>
    public partial class EditableTextBlock : UserControl
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="EditableTextBlock"/> instance.
        /// </summary>
        public EditableTextBlock()
        {
            InitializeComponent();
        }

        #endregion

        #region Dependency Property Fields

        /// <summary>
        /// Identifies the <see cref="IsEditable"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register(
            "IsEditable", typeof(bool), typeof(EditableTextBlock), new PropertyMetadata(true));

        /// <summary>
        /// Identifies the <see cref="IsInEditMode"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty IsInEditModeProperty = DependencyProperty.Register(
            "IsInEditMode", typeof(bool), typeof(EditableTextBlock), new PropertyMetadata(false, IsInEditMode_PropertyChanged));

        #endregion

        #region Routed Events Fields

        /// <summary>
        /// Identifies the <see cref="IsInEditModeChanged"/> routed event.
        /// </summary>
        public static readonly RoutedEvent IsInEditModeChangedEvent = EventManager.RegisterRoutedEvent(
            "IsInEditModeChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(EditableTextBlock));

        #endregion

        #region Events

        /// <summary>
        /// Occurs when the value of the <see cref="IsInEditMode"/> property has changed.
        /// </summary>
        public event RoutedEventHandler IsInEditModeChanged
        {
            add { AddHandler(IsInEditModeChangedEvent, value); }
            remove { RemoveHandler(IsInEditModeChangedEvent, value); }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when the value of the <see cref="IsInEditMode"/> dependency property has changed.
        /// </summary>
        /// <param name="d">The dependency object whose property value has changed</param>
        /// <param name="e">Event data</param>
        private static void IsInEditMode_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = (EditableTextBlock)d;

            if ((bool)e.NewValue == true)
            {
                if (!control.IsEditable)
                    control.IsInEditMode = false;

                control.PART_Block.Visibility = Visibility.Collapsed;
                control.PART_Box.Visibility = Visibility.Visible;
                control.oldText = control.PART_Box.Text;
                control.PART_Box.Focus();
                control.PART_Box.SelectAll();
            }
            else
            {
                control.PART_Block.Visibility = Visibility.Visible;
                control.PART_Box.Visibility = Visibility.Collapsed;
            }

            control.RaiseEvent(new RoutedEventArgs(EditableTextBlock.IsInEditModeChangedEvent, control));
        }

        /// <summary>
        /// Occurs when the text box loses focus.
        /// </summary>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void PART_Box_LostFocus(object sender, RoutedEventArgs e)
        {
            var item = (ViewModel.IProjectExplorerItem)DataContext;
            if (item.Parent != null && item.Parent.Children.Any(child => child.Name == PART_Box.Text && child != item))
                PART_Box.Text = oldText;
            IsInEditMode = false;
        }

        /// <summary>
        /// Occurs when the user presses key inside the text box (while the control is in the edit mode).
        /// </summary>
        /// <param name="sender">Object where the event handler is attached</param>
        /// <param name="e">Event data</param>
        private void PART_Box_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                var item = (ViewModel.IProjectExplorerItem)DataContext;
                if (item.Parent != null && item.Parent.Children.Any(child => child.Name == PART_Box.Text && child != item))
                {
                    System.Media.SystemSounds.Beep.Play();
                    PART_Box.SelectAll();
                }
                else
                    IsInEditMode = false;
                e.Handled = true;
            }
            else if (e.Key == Key.Escape)
            {
                IsInEditMode = false;
                PART_Box.Text = oldText;
                e.Handled = true;
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the value indicating whether the content of the control can be edited or not.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Boolean"/>
        /// </para>
        /// <para>
        /// Indicates whether the content of the control can be edited or not.<br/>
        /// If this value is <see langword="false"/> the control cannot be in edit mode
        /// (i.e. the <see cref="IsInEditMode"/> property cannot be <see langword="true"/>).
        /// </para>
        /// </value>
        public bool IsEditable
        {
            get { return (bool)GetValue(IsEditableProperty); }
            set { SetValue(IsEditableProperty, value); }
        }

        /// <summary>
        /// Gets or sets the value indicating whether the control is currently in edit mode.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Boolean"/>
        /// </para>
        /// <para>
        /// Indicates whether the control is currently in the edit mode.
        /// </para>
        /// </value>
        public bool IsInEditMode
        {
            get { return (bool)GetValue(IsInEditModeProperty); }
            set { SetValue(IsInEditModeProperty, value); }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the old text.
        /// </summary>
        private string oldText;

        #endregion
    }
}
